#!/usr/bin/env python
#
#       Client.py
#       
#       Copyright 2009 Ben Davis <its.hip@gmail.com>, Pete Lord <mr.petelord@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import os
import pygame
import socket
import subprocess
import sys
import time

# pygame setup
pygame.init()
pygame.display.set_caption("TurnShip: Skies of Ourl","TurnShip")
pygame.display.set_icon(pygame.image.load(os.path.join('Data', 'Icon.png')))
pygame.key.set_repeat(100, 30)


class Client(list):
    """List to hold all the widgets on-screen"""
    
    def setup(self):
        flags = (pygame.FULLSCREEN * Options.FULLSCREEN) | pygame.DOUBLEBUF | pygame.HWSURFACE
        self.canvas = pygame.display.set_mode((Options.SCREEN_WIDTH, Options.SCREEN_HEIGHT), flags)
        self.rect = pygame.Rect(0, 0, Options.SCREEN_WIDTH, Options.SCREEN_HEIGHT)
        self.ship_graphics = Graphics.ShipGraphics()
        self.tile_graphics = Graphics.TileGraphics()
        self.button_graphics = Graphics.ButtonGraphics()
        self.graphics = Graphics.Graphics()
        self.sounds = Sounds.Sounds()
        self.database = Database.Database()
        self.clock = pygame.time.Clock()
        self.server = None
        self.game = None
        # Focused widget
        self.focused = None
        # Set to True when focus is set this frame
        self.focus_set = False
        # Create an events handler
        self.events = Events.Events()
        # Bind 
        self.events.bind(self.toggle_fullscreen, type=pygame.KEYDOWN, key=pygame.K_f, mod=pygame.KMOD_LCTRL)
        self.events.bind(self.toggle_fullscreen, type=pygame.KEYDOWN, key=pygame.K_f, mod=pygame.KMOD_RCTRL)
        self.events.bind(self.toggle_fullscreen, type=pygame.KEYDOWN, key=pygame.K_f, mod=pygame.KMOD_CTRL)
        self.events.bind(self.handle_resize, type=pygame.VIDEORESIZE)
        self.events.bind(sys.exit, type=pygame.QUIT)
        
        self.sounds.music('Cogs.ogg', -1)
        self.host = ""
        self.reasoned = False
        
        self.chathistory = []
        self.chat = 0
        
        self.show_menu()
        
        self.queue = list()
    
    
    def addQ(self, thing):
        self.queue.append(thing);
    
        
    def join(self, host, port):
        if self.server:
            # Confusing terminology, but since Socket.Server runs as a thread,
            # When we want it to close we ask the thread to 'join' the main
            # thread
            self.server.join()
        if self.game:
            # Close the currently running game
            self.game.close()
            self.chathistory = []
            self.chat = 0
        Interfaces.TimedBox(self, "Connecting to " + host + ":" + str(port))
        self.server = Socket.Server((host, port))
        self.server.start()
        
    def run(self):
        clicking = None, None
        hovering = None, None
        # Start main loop
        while True:
            for thing in self.queue:
                exec(thing)
                self.queue.remove(thing)
            # Aim for 40 frames-per-second
            self.clock.tick(40)
            # If we're connected to a server
            if self.server:
                # If the server disconnects
                if self.server.disconnected:
                    #self.game.close()
                    print "severed."
                    done = True
                    '''for widget in self:
                        print widget
                        try:
                            if widget.name == "screen":
                                print "Screen, go to main."
                                widget.main()
                                done == True
                        except AttributeError:
                            print "AttErr: " + widget
                            pass
                    '''
                    
                    try:
                        self.game.close()
                    except AttributeError:
                        print "self.game.close, Client.py line 123, died of attribute error."
                        
                        pass
                    
                    if not self.reasoned:
                        self.show_menu()
                        Interfaces.TimedBox(self, "Disconnected from " + host + " (unknown reason)")
                    else:
                        self.show_menu()
                        
                    print "Reasoned?", self.reasoned
                    for widget in self:
                            print widget
                    self.host = ""
                    self.server.connected = False
                    self.server.disconnected = False
                    self.sounds.music('Cogs.ogg', -1, True)
                # Parse all packets
                for packet in self.server.get():
                    packet()
                    if isinstance(packet, ToClient.Sync):
                        host, port = self.server.socket.getpeername()
                        self.host = host
                        try:
                            self.remove(self.menu)
                        except ValueError:
                            pass
                        self.focus(self.game)
                        self.events.bind(self.show_menu, type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
                        Interfaces.TimedBox(self, "Connected to " + host + ":" + str(port) + " successfully")
                        
                # If we've just tried to connect
                if self.server.connected:
                    host, port = self.server.addr
                    if isinstance(self.server.connected, socket.timeout):
                        Interfaces.TimedBox(self, "Timed out connecting to " + host + ":" + str(port))
                        self.server = None
                    elif isinstance(self.server.connected, socket.error):
                        Interfaces.TimedBox(self, "Could not connect to " + host + ":" + str(port))
                        self.server = None
                    try:
                        self.server.connected = False
                    except AttributeError:
                        pass
                        #I guess we're disconnected.
                    
            # Set to True when focus is set this frame
            self.focus_set = False
            
            # If we have a widget grabbing input
            if self.focused is not None:
                # But it isn't in the parent any more (i.e, it's been
                # deleted)
                if not (self.focused in self.focused.parent):
                    # Release input grab
                    self.focus(None)

            # Parse MOUSEBUTTONDOWN and MOUSEMOTION events first
            # When the player first clicks a mouse button
            for event in pygame.event.get(pygame.MOUSEBUTTONDOWN):
                if event.button == 1:
                    over, pos = self.over(event.pos)
                    if over is not None:
                        clicking = over, pos
                        # Widget.click returns True if widget wants focus
                        if over.click(pos) == True:
                            # If widget wants to grab input focus, then
                            # stop taking events
                            self.focus(over)
                    else:
                        self.focus(None)
                else:
                    if self.focused is not None:
                        self.focused.events.parse(event)

            # If the player moves the mouse
            for event in pygame.event.get(pygame.MOUSEMOTION):
                # Get the widget under the mouse cursor
                over, pos = self.over(event.pos)
                if hovering[0]:
                    if hovering[0] != over:
                        hovering[0].unhover()
                if over != None:
                    if over == clicking[0]:
                        over.drag(pos)
                    else:
                        over.hover(pos)
                hovering = over, pos
                
            # If the player releases a mouse button
            for event in pygame.event.get(pygame.MOUSEBUTTONUP):
                # Get the widget under the mouse cursor
                over, pos = self.over(event.pos)
                if over != None:
                    if over == clicking[0]:
                        if pos == clicking[1]:
                            over.clicked(pos)
                        else:
                            over.dragged(pos)
                clicking = None, None
            
            # Possible optimisation: only take the last MOUSEMOTION event
            # per frame?
                
            # For all other events on the queue
            for event in pygame.event.get():
                # If we have a focused widget
                if self.focused is not None:
                    # If event doesn't get parsed by focused events handler
                    if not self.focused.events.parse(event):
                        # Send the event to the focused widget to be parsed
                        self.events.parse(event)
                # If we don't have focused widget
                else:
                    # Send the event to the focused widget to be parsed
                    self.events.parse(event)
                        
            self.update()
            self.canvas.fill((0, 0, 0))
            self.draw()
            
            # Flip display buffer
            pygame.display.flip()
        
    def draw(self):
        for widget in self:
            surface, position = widget.draw()
            left, top = position.topleft
            if left < 0:
                left += self.rect.width - position.width
            if top < 0:
                top += self.rect.height - position.height
            self.canvas.blit(surface, (left, top))
    
    def redraw(self):
        return
    
    def blit(self, (surface, position)):
        # If the sprite is on screen
        if position.colliderect(self.rect):
            self.canvas.blit(surface, position)
    
    def update(self):
        for widget in self:
            widget.update()
    
    def over(self, pos):
        """Return widget at (x, y)
        
        If no widget at (x, y) return None"""
        for widget in reversed(self):
            over, local_pos = widget.over(pos)
            if over is not None:
                return over, local_pos
        return None, None
        
    def toggle_fullscreen(self, *args):
        flags = self.canvas.get_flags() ^ pygame.FULLSCREEN
        if flags & pygame.FULLSCREEN == pygame.FULLSCREEN:
            size = pygame.display.list_modes()[0]
            self.rect.w = size[0]
            self.rect.h = size[1]
            resized = "Fullscreen "
        else:
            size = (Options.SCREEN_WIDTH, Options.SCREEN_HEIGHT)
            self.rect.w = Options.SCREEN_WIDTH
            self.rect.h = Options.SCREEN_HEIGHT
            resized = "Windowed "
        # Reinitialise 'canvas' surface
        self.canvas = pygame.display.set_mode(size, flags)
        resized += str(self.canvas.get_size())
        Interfaces.TimedBox(self, resized)

    def handle_resize(self, event):
        Options.SCREEN_WIDTH, Options.SCREEN_HEIGHT = self.rect.size = event.w, event.h
        self.canvas = pygame.display.set_mode((Options.SCREEN_WIDTH, Options.SCREEN_HEIGHT), self.canvas.get_flags())

    def focus(self, widget):
        if not self.focus_set:
            last = self.focused
            # If we're resetting focus
            if widget == None:
                if self.game:
                    # By default, focus self.game
                    self.focused = self.game
                else:
                    self.focused = None
                    return
            else:
                self.focused = widget
                self.focused.focus()
            if last:
                last.unfocus()
            self.focus_set = True
            
    def show_menu(self, *args):
        # Show main menu
        self.menu = Interfaces.MenuContainer(self)
        self.focus(self.menu)
        
    def kicked(self, reason):
        self.server.connected = False
        self.server.disconnected = True
        #self.server = None
        '''for widget in self:
            print widget
            print widget.name
            try:
                if widget.name == "screen":
                    widget.main()
            except AttributeError:
                print "screen not found(client.py 321)"
                pass'''
        try:
            self.game.close()
        except AttributeError:
            print "AttrErr: self.game.close, Client.py line 322"
            pass
        self.show_menu()
        Interfaces.TimedBox(self, "Disconnected from " + self.host + " ("+reason+")")
        print "Kicked from", self.host
        self.reasoned = True
        self.host = ""
        
        
Client = lambda single_Client=Client(): single_Client

import Database
import Effects
import Events
import Graphics
import Interfaces
import Maps
import Options
import Orders
import Ships
import Socket
import Sounds
import ToClient
import ToServer
